package enumerable;

import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

@SuppressWarnings("serial")
public class HashSet<T> extends java.util.HashSet<T> implements Set<T> {
	@Override
	public <K> Enumerable<T> select(Selector<T> select) {		
		Set<T> set = new HashSet<T>();
		
		for (T elem : this) {
			if (select.predicate(elem)) {
				set.add(elem);
			}
		}
		
		return set;
	}

	@Override
	public <K> Enumerable<K> project(Projector<T, K> project) {
		Set<K> set = new HashSet<K>();
		
		for (T elem : this) {
			set.add(project.project(elem));
		}
		
		return set;
	}

	@Override
	public T single() {
		T elem = singleOrNull();
		if (elem == null) {
			throw new MultipleElementsException();
		}
		
		return elem;
	}

	@Override
	public T single(Selector<T> select) {
		return this.select(select).single();
	}

	@Override
	public T singleOrNull(Selector<T> select) {
		return this.select(select).singleOrNull();
	}

	@Override
	public T first() {
		T elem = firstOrNull();
		if (elem == null) {
			throw new NoElementException();
		}
		
		return elem;
	}

	@Override
	public T first(Selector<T> select) {
		return this.select(select).first();
	}

	@Override
	public T firstOrNull(Selector<T> select) {
		return this.select(select).firstOrNull();
	}

	@Override
	public T singleOrNull() {
		T elem = null;
		
		if (this.size() == 1) {
			elem = this.first();
		}
		
		return elem;
	}

	@Override
	public T firstOrNull() {
		T elem = null;
		
		if (this.size() > 0) {
			elem = this.iterator().next();
		}
		
		return elem;
	}

	@Override
	public LinkedList<T> toList() {
		LinkedList<T> list = new LinkedList<T>();
		
		for (T elem : this) {
			list.add(elem);
		}
		
		return list;
	}

	@Override
	public boolean any(Selector<T> select) {
		boolean found = false;
		for (T elem : this) {
			if (select.predicate(elem)) {
				found = true;
				break;
			}
		}
		
		return found;
	}

	@Override
	public boolean all(Selector<T> select) {
		return count(select) == this.size();
	}

	@Override
	public int count(Selector<T> select) {
		return select(select).count();
	}

	@Override
	public int count() {
		return this.size();
	}
	
	@Override
	public <K> Enumerable<K> projectMany(Projector<T, Enumerable<K>> project) {
		Set<K> list = new HashSet<K>();
		
		for (T elem : this) {
			for (K subElem : project.project(elem)) {
				list.add(subElem);
			}
		}
		
		return list;
	}
	
	@Override
	public void forEach(Mapper<T> mapper) {
		for (T elem : this) {
			mapper.map(elem);
		}
	}
}
